package com.yoyo.framework.shiro.web.session;

import com.yoyo.common.constant.ShiroConstants;
import com.yoyo.common.utils.DateUtils;
import com.yoyo.common.utils.StringUtils;
import com.yoyo.common.utils.bean.BeanUtils;
import com.yoyo.common.utils.spring.SpringUtils;
import com.yoyo.framework.shiro.session.OnlineSession;
import com.yoyo.system.domain.SysUserOnline;
import com.yoyo.system.service.ISysUserOnlineService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * @author zyl
 * @Date 2021/7/10 -21:35
 * 主要是在此如果会话的属性修改了 就标识下其修改了 然后方便OnlineSessionDao 同步
 */
@Slf4j
public class OnlineWebSessionManager extends DefaultWebSessionManager {
    @Override
    public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
        super.setAttribute(sessionKey, attributeKey, value);
        if (value != null && needMarkAttributeChanged(attributeKey)) {
            OnlineSession session = getOnlineSession(sessionKey);
            session.markAttributeChanged();
        }
    }

    @Override
    public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
        Object removed = super.removeAttribute(sessionKey, attributeKey);
        if (removed != null) {
            OnlineSession session = getOnlineSession(sessionKey);
            session.isAttributeChanged();
        }
        return removed;
    }

    /**
     * 验证Session 是否有效 用于删除过期Session
     */
    @Override
    public void validateSessions() {
        log.info("invalidation sessions...");
        int invalidCount = 0;
        int timeout = (int) this.getGlobalSessionTimeout();
        Date expireDate = DateUtils.addMilliseconds(new Date(), 0 - timeout);
        ISysUserOnlineService userOnlineService = SpringUtils.getBean(ISysUserOnlineService.class);
        List<SysUserOnline> userOnlineList = userOnlineService.selectOnlineByExpired(expireDate);
        List<String> needOfflineIdList = new ArrayList<>();
        for (SysUserOnline sysUserOnline : userOnlineList) {
            try {
                SessionKey key = new DefaultSessionKey(sysUserOnline.getSessionId());
                Session session = retrieveSession(key);
                if (session != null) {
                    throw new InvalidSessionException();
                }
            } catch (InvalidSessionException e) {
                boolean expired = (e instanceof ExpiredSessionException);
                log.debug("Invalidated session with id{}{} (expired :stopped)", sysUserOnline.getSessionId(), expired);
                invalidCount++;
                needOfflineIdList.add(sysUserOnline.getSessionId());
            }
        }
        if (invalidCount > 0) {
            log.info("Finished invalidation session {} sessions were stopped.", invalidCount);
        } else {
            log.info("Finished invalidation session  No sessions were stopped.");
        }
    }

    @Override
    protected Collection<Session> getActiveSessions() {
        throw new UnsupportedOperationException("getActiveSessions method not supported");
    }

    private OnlineSession getOnlineSession(SessionKey sessionKey) {
        OnlineSession session = null;
        Session obj = doGetSession(sessionKey);
        if (StringUtils.isNotNull(obj)) {
            session = new OnlineSession();
            BeanUtils.copyBeanProp(session, obj);
        }
        return session;
    }

    private boolean needMarkAttributeChanged(Object attributeKey) {
        if (attributeKey == null) {
            return false;
        }
        String attributeKeyStr = attributeKey.toString();

        if (attributeKeyStr.startsWith("org.springframework")) {
            return false;
        }

        if (attributeKeyStr.startsWith("javax.servlet")) {
            return false;
        }
        if (attributeKeyStr.equals(ShiroConstants.CURRENT_USERNAME)) {
            return false;
        }
        return true;
    }
}
